package com.xdjd.mobile.business.service;


import com.gitee.easyopen.exception.ApiException;
import com.xdjd.mobile.business.constant.ErrorCodeEnum;
import com.xdjd.mobile.business.constant.Msgs;
import com.xdjd.mobile.business.constant.RedisConstant;
import com.xdjd.mobile.business.entity.param.my.VerifyCodeParam;
import com.xdjd.mobile.business.entity.vo.VerifyCodeBizTypeEnum;
import com.xdjd.mobile.business.util.ParamCheckUtils;
import com.xdjd.mobile.business.util.VerifyCodeGenerateUtil;
import com.xdjd.mobile.third.SendSmsService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;


@Service
public class VerifyCodeService {

    private static final Logger logger = LoggerFactory.getLogger(VerifyCodeService.class);

    @Autowired
    private RedisTemplate<String, String> template;
    @Autowired
    private SendSmsService sendSmsService;

    public boolean sendVerifyCode(VerifyCodeParam codeParam) {
        // 参数校验
        checkParam(codeParam);
        String checkMsg = checkSMS(codeParam);
        if (null != checkMsg) {
            return false;
        }
        VerifyCodeBizTypeEnum bizType = VerifyCodeBizTypeEnum.getByCode(Integer.valueOf(codeParam.getBizType()));
        sendSMS(codeParam.getMobile(), bizType);
        return true;
    }


    private String checkSMS(VerifyCodeParam codeParam) {

        // 1.检测：同一手机号，1天不能超过5次验证码。
        int result = checkSendPhoneNumSum(codeParam);
        if (result == -1) {
            return "验证码发送频繁，请24小时后再试";
        }

        // 2.检测：同一IP,1天不能超过20次验证码
        result = checkSendIPNumSum(codeParam);
        if (result == -1) {
            return "验证码发送频繁，请24小时后再试";
        }

        VerifyCodeBizTypeEnum bizType = VerifyCodeBizTypeEnum.getByCode(Integer.valueOf(codeParam.getBizType()));
        // 登录--短信验证码校验
        if (bizType.equals(VerifyCodeBizTypeEnum.LOGIN)) {
            // 登录号码 只能发3次
            result = sendRegisterSMSCheck(codeParam);
            if (result == -1) {
                return "验证码发送次数已达上限";
            }
        }
        return null;
    }

    /**
     * 参数校验
     */
    private void checkParam(VerifyCodeParam codeParam) {
        // 参数校验
        ParamCheckUtils.checkExist(codeParam);
        // 1：短信验证码 2：图形验证码
        //ParamCheckUtils.checkParamExist(codeParam.getCodeType(), "验证码类型不能为空");
        // 10：注册；11：登录 12：找回密码 13：借款验证码
        ParamCheckUtils.checkParamExist(codeParam.getBizType(), "业务类型不能为空");
        VerifyCodeBizTypeEnum bizType = VerifyCodeBizTypeEnum.getByCode(Integer.valueOf(codeParam.getBizType()));
        if (null == bizType) {
            ParamCheckUtils.checkParamExist(codeParam.getMobile(), "业务类型错误");
        }
        ParamCheckUtils.checkParamExist(codeParam.getMobile(), "手机号码不能为空");
        ParamCheckUtils.checkMobileNo(codeParam.getMobile(), "手机号码错误");
    }

    /**
     * 检测：同一手机号，1天不能超过5次验证码。
     */
    private int checkSendPhoneNumSum(VerifyCodeParam codeParam) {
        String phoneNumKey = RedisConstant.setSendPhoneNumKey(0, codeParam.getMobile());
        Object phoneNumCheckVlue = template.opsForValue().get(phoneNumKey);
        if (null != phoneNumCheckVlue) {
            int sum = Integer.valueOf(phoneNumCheckVlue.toString());
            if (sum >= 5) {
                logger.info("ip:{},手机号{},1天内已发送{}条短信,超过5条验证码", codeParam.getIp(), codeParam.getMobile(), sum);
                return -1;
            }
        }
        return 0;
    }

    /**
     * 检测：同一IP,1天不能超过20次验证码。
     */
    private int checkSendIPNumSum(VerifyCodeParam codeParam) {
        String ipNumKey = RedisConstant.setSendPhoneNumKey(1, codeParam.getIp());
        Object ipNumCheckVlue = template.opsForValue().get(ipNumKey);
        if (null != ipNumCheckVlue) {
            int sum = Integer.valueOf(ipNumCheckVlue.toString());
            if (sum >= 20) {
                logger.info("ip:{},手机号{},1天内已发送{}条短信,超过了20次验证码", codeParam.getIp(), codeParam.getMobile(), sum);
                return -1;
            }
        }

        return 0;
    }

    /**
     * 检测：注册号码 ,终生只能发3次
     */
    private int sendRegisterSMSCheck(VerifyCodeParam codeParam) {
        String regNumKey = RedisConstant.setSendPhoneNumKey(2, codeParam.getMobile());
        Object regNumCheckVlue = template.opsForHash().get(RedisConstant.IPHONE_REG_SEND_PHONE_MAP, regNumKey);
        if (null != regNumCheckVlue) {
            int sum = Integer.valueOf(regNumCheckVlue.toString());
            if (sum >= 3) {
                logger.info("ip:{},手机号{},注册已发送{}条短信,超过了3次验证码", codeParam.getIp(), codeParam.getMobile(), sum);
                return -1;
            }
        }
        return 0;
    }


    /**
     * 保存 短信 验证码
     */
    private Boolean saveSmsCode(String mobile, String bizType, String smsCode) {
        try {
            String smsCodeKey = bizType + ":" + mobile;
            template.opsForValue().set(smsCodeKey, smsCode);
            template.expire(smsCodeKey, 600, TimeUnit.SECONDS);
            return true;
        } catch (Exception e) {
            logger.error("短信验证码 redis 异常: {}", e);
            return false;
        }
    }

    /**
     * 发送短信验证码
     */
    private void sendSMS(String mobile, VerifyCodeBizTypeEnum bizType) {
        String verifyCode = VerifyCodeGenerateUtil.getNunVerifyCode();
        String smsMsg = String.format(Msgs.LOGIN_SMS, verifyCode);
        boolean sendResult = sendSmsService.sendSms(mobile, smsMsg);
        // 验证码发送失败
        if (!sendResult) {
            throw new ApiException(ErrorCodeEnum.send_sms_error.getDesc(), ErrorCodeEnum.send_sms_error.getCode());
        }
        logger.info("手机号: {} 发送验证码: {}", mobile, verifyCode);
        saveSmsCode(mobile, String.valueOf(bizType.getCode()), verifyCode);
    }

}
